/*****************************************************************************
 * Copyright (C) Stacy Curl. All rights reserved.                            *
 * ------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the BSD      *
 * style license a copy of which has been included with this distribution in *
 * the LICENSE.txt file.                                                     *
 *****************************************************************************/
package picounit.features.ruby_string.delayed;

import moved.org.objectweb.asm.Attribute;
import moved.org.objectweb.asm.Label;
import moved.org.objectweb.asm.MethodVisitor;
import moved.org.objectweb.asm.Opcodes;
import picounit.Should;
import picounit.Test;

public class MethodInstructionTests implements Test, Opcodes {
	private final Should should;

	private final Collaborators collaborators;
	
	interface Collaborators {
		MethodVisitor methodVisitor();
		DelayedAnnotationMutator delayedAnnotationMutator();
	}

	public MethodInstructionTests(Collaborators collaborators, Should should) {
		this.collaborators = collaborators;
		this.should = should;
	}
	
	public void shouldVisitAnnotationWhenAnnotationAcceptingAMethodVisitor() {
		collaborators.methodVisitor().visitAnnotation("name", false);

		should.expectAboveWhenTheFollowingOccurs();

		new Annotation("name", false, collaborators.delayedAnnotationMutator())
			.accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitAnnotationDefaultWhenAnnotationDefaultAcceptsAMethodVisitor() {
		collaborators.methodVisitor().visitAnnotationDefault();
		
		should.expectAboveWhenTheFollowingOccurs();

		new AnnotationDefault(null).accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitAttributeWhenAttributeInstructionAcceptsAMethodVisitor() {
		Attribute attribute = new Attribute("attribute") {};

		collaborators.methodVisitor().visitAttribute(attribute);
		
		should.expectAboveWhenTheFollowingOccurs();

		new AttributeInstruction(attribute).accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitCodeWhenCodeInstructionAcceptsAMethodVisitor() {
		collaborators.methodVisitor().visitCode();

		should.expectAboveWhenTheFollowingOccurs();

		CodeInstruction.INSTANCE.accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitEndWhenEndInstructionAcceptsAMethodVisitor() {
		collaborators.methodVisitor().visitEnd();

		should.expectAboveWhenTheFollowingOccurs();

		EndInstruction.INSTANCE.accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitFieldInsnWhenFieldInstructionAcceptsAMethodVisitor() {
		collaborators.methodVisitor().visitFieldInsn(AALOAD, "owner", "name", "desc");
		
		should.expectAboveWhenTheFollowingOccurs();
		
		new FieldInstruction(AALOAD, "owner", "name", "desc")
			.accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitIincInsnWhenIincInstructionAcceptsAMethodVisitor() {
		collaborators.methodVisitor().visitIincInsn(12345, 54321);
		
		should.expectAboveWhenTheFollowingOccurs();
		
		new IincInstruction(12345, 54321).accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitIntInsnWhenIntInstructionAcceptsAMethodVisitor() {
		collaborators.methodVisitor().visitIntInsn(AALOAD, 12345);

		should.expectAboveWhenTheFollowingOccurs();

		new IntInstruction(AALOAD, 12345).accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitMethodInsnWhenInvokeMethodInstructionAcceptsAMethodVisitor() {
		collaborators.methodVisitor().visitMethodInsn(AALOAD, "owner", "name", "desc");

		should.expectAboveWhenTheFollowingOccurs();

		new InvokeMethodInstruction(AALOAD, "owner", "name", "desc")
			.accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitJumpInsnWhenJumpInstructionAcceptsAMethodVisitor() {
		Label label = new Label();
		collaborators.methodVisitor().visitJumpInsn(AALOAD, label);
		
		should.expectAboveWhenTheFollowingOccurs();
		
		new JumpInstruction(AALOAD, label)
			.accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitLabelInsnWhenLabelInstructionAcceptsAMethodVisitor() {
		Label label = new Label();
		collaborators.methodVisitor().visitLabel(label);
		
		should.expectAboveWhenTheFollowingOccurs();
		
		new LabelInstruction(label).accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitLdcInsnWhenLdcInstructionAcceptsAMethodVisitor() {
		Object constant = new Object();
		collaborators.methodVisitor().visitLdcInsn(constant);
		
		should.expectAboveWhenTheFollowingOccurs();
		
		new LdcInstruction(constant).accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitLineNumberWhenLineNumberInstructionAcceptsAMethodVisitor() {
		Label startLabel = new Label();
		collaborators.methodVisitor().visitLineNumber(12345, startLabel);
		
		should.expectAboveWhenTheFollowingOccurs();
		
		new LineNumberInstruction(12345, startLabel).accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitLocalVariableWhenLocalVariableInstructionAcceptsAMethodVisitor() {
		Label start = new Label();
		Label end = new Label();
		
		collaborators.methodVisitor().visitLocalVariable("name", "desc", "signature", start, end, 12345);
		
		should.expectAboveWhenTheFollowingOccurs();
		
		new LocalVariableInstruction("name", "desc", "signature", start, end, 12345)
			.accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitLookupSwitchInsnWhenLookupSwitchInstructionAcceptsAMethodVisitor() {
		Label dflt = new Label();
		int[] keys = {1,2};
		Label[] labels = {new Label(), new Label()};
		
		collaborators.methodVisitor().visitLookupSwitchInsn(dflt, keys, labels);
		
		should.expectAboveWhenTheFollowingOccurs();
		
		new LookupSwitchInstruction(dflt, keys, labels).accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitMaxsWhenMaxsInstructionAcceptsAMethodVisitor() {
		collaborators.methodVisitor().visitMaxs(12345, 54321);
		
		should.expectAboveWhenTheFollowingOccurs();
		
		new MaxsInstruction(12345, 54321).accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitMultiANewArrayInsnWhenMultiANewArrayInstructionAcceptsAMethodVisitor() {
		collaborators.methodVisitor().visitMultiANewArrayInsn("desc", 12345);
		
		should.expectAboveWhenTheFollowingOccurs();
		
		new MultiANewArrayInstruction("desc", 12345).accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitParameterAnnotationWhenParameterAnnotationAcceptsAMethodVisitor() {
		collaborators.methodVisitor().visitParameterAnnotation(12345, "name", false);
		
		should.expectAboveWhenTheFollowingOccurs();
		
		new ParameterAnnotation(12345, "name", false, collaborators.delayedAnnotationMutator())
			.accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitTableSwitchInsnWhenTableSwitchInstructionAcceptsAMethodVisitor() {
		Label dflt = new Label();
		Label[] labels = {new Label(), new Label()};
		collaborators.methodVisitor().visitTableSwitchInsn(12345, 54321, dflt, labels);

		should.expectAboveWhenTheFollowingOccurs();

		new TableSwitchInstruction(12345, 54321, dflt, labels).accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitTryCatchBlockWhenTryCatchBlockInstructionAcceptsAMethodVisitor() {
		Label start = new Label();
		Label end = new Label();
		Label handle = new Label();
		
		collaborators.methodVisitor().visitTryCatchBlock(start, end, handle, "type");
		
		should.expectAboveWhenTheFollowingOccurs();

		new TryCatchBlockInstruction(start, end, handle, "type").accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitTypeInsnWhenTypeInstructionAcceptsAMethodVisitor() {
		collaborators.methodVisitor().visitTypeInsn(AALOAD, "desc");
		
		should.expectAboveWhenTheFollowingOccurs();
		
		new TypeInstruction(AALOAD, "desc").accept(collaborators.methodVisitor());
	}
	
	public void shouldVisitVarInsnWhenVarInstructionAcceptsAMethodVisitor() {
		collaborators.methodVisitor().visitVarInsn(AALOAD, 12345);
		
		should.expectAboveWhenTheFollowingOccurs();
		
		new VarInstruction(AALOAD, 12345).accept(collaborators.methodVisitor());
	}
	
	public void should() {
		Label start = new Label();
		Label end = new Label();
		
//		mockLdcInstruction.updateLocalVariable("name", "desc", "signature", start, end, 12345);
		
//		should.expectAboveWhenTheFollowingOccurs();

		MethodInstruction localVariable =
			new LocalVariableInstruction("name", "desc", "signature", start, end, 12345);
		
		MethodInstruction ldcInstruction = new LdcInstruction("Blahh blah");
		
		ldcInstruction.inform(localVariable);
	}
}
